﻿<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
	<title>Automation curve editor</title>

	<script src="/ext/js/prototype.js" type="text/javascript"></script>

	<script src="/ext/js/Seq.Common.js" type="text/javascript"></script>

	<script src="/ext/js/Seq.State.js" type="text/javascript"></script>

	<script src="/ext/js/Seq.UI.js" type="text/javascript"></script>

	<script src="/ext/js/Seq.Notifications.js" type="text/javascript"></script>

	<script src="/ext/js/Seq.Viewport.js" type="text/javascript"></script>

	<script src="/ext/js/Seq.Pattern.js" type="text/javascript"></script>

	<style type="text/css">
		html
		{
			margin: 0;
			padding: 0;
			border: 0;
		}
		body
		{
			margin: 0;
			padding: 0;
			border: 0;
			background-color: #fff;
			font: 8pt Arial;
		}
		a
		{
			color: #666;
			font-weight: bold;
			text-decoration: none;
		}
		#header
		{
			position: absolute;
			left: 0px;
			top: 0px;
			right: 0px;
			height: 40px;
			background-color: #888;
			color: #fff;
		}
		#header p
		{
			margin: 10px;
		}
		#main
		{
			position: absolute;
			left: 0px;
			top: 40px;
			right: 0px;
			bottom: 0px;
			background-color: #444;
			color: #ff0;
		}
		#main .inner
		{
		}
		#pthelp
		{
			position: absolute;
			right: 5px;
			top: 5px;
			padding: 5px 10px; /*background-color: #555; */
			color: #888;
		}
		a.ptdim
		{
			display: block;
			position: absolute;
			width: 10px;
			height: 10px;
			background-color: #555;
		}
		a.pt
		{
			display: block;
			position: absolute;
			width: 10px;
			height: 10px;
			background-color: #888;
		}
		a.pt:hover
		{
			background-color: #fff;
		}
		a.pt span
		{
			position: absolute;
			left: -40px;
			width: 100px;
			text-align: center;
			top: -15px;
			font-size: 8pt;
			color: #666;
		}
		a.pt:hover span
		{
			color: #ccc;
		}
		#ptbg
		{
			position: absolute;
			border: 1px dotted #666;
		}
		.ptguideline
		{
			position: absolute;
			left: 0px;
			top: 0px;
			width: 1px;
			background-color: #777;
		}
		.ptcurveline
		{
			position: absolute;
			left: 0px;
			top: 0px;
			width: 1px;
			background-color: #ff7;
		}
	</style>

	<script type="text/javascript">

		var g_Loading = true;
		var g_PatternID = '';
		var g_Channel = 0;
		var g_SessionID = '';
		var g_InstrumentID = '';
		var g_PatternData = {};
		var g_PatternList = {};
		var g_Viewport = null;
		var g_Interp = null;
		var g_Selected = -1;
		var g_AnyMovement = false;
		var g_CurveData = [];

		function onReloadPatternList(r) {
			var data = r.responseText.evalJSON(false);
			var el = $('patterns');
			var sel = Seq.UI.GetDropdownValue(el, g_PatternID);
			el.options.length = 0;
			for (var j = 0; j < data.patterns.length; j++) {
				var pat = data.patterns[j];
				el.options[j] = new Option(pat.name, pat.id);
			}
			Seq.UI.SetDropdownValue(el, sel);
		}

		function reloadPatternList() {
			new Ajax.Request('/api/call', { parameters: { 'getpatternlist': g_SessionID }, method: 'get', onSuccess: onReloadPatternList });
		}

		function onReloadAutomationChannelList(r) {
			var transl = {};
			var data = r.responseText.evalJSON(false);
			for (var j = 0; j < data.channels.length; j++) {
				var chan = data.channels[j];
				transl[chan.id] = chan.name;
			}
			var el = $('channels');
			var sel = Seq.UI.GetDropdownValue(el, g_Channel);
			el.options.length = 0;
			for (var j = 0; j < 127; j++) {
				var nam = '#' + j;
				if (transl[j] && transl[j] != '')
					nam += ' ' + transl[j];
				el.options[j] = new Option(nam, j.toString());
			}
			Seq.UI.SetDropdownValue(el, sel);
		}

		function reloadAutomationChannelList() {
			new Ajax.Request('/api/call', { parameters: { 'getautomationchannels': g_InstrumentID }, method: 'get', onSuccess: onReloadAutomationChannelList });
		}

		function onReloadPattern(r) {
			g_PatternData = r.responseText.evalJSON(false);
			console.log(g_PatternData);
			g_Loading = false;
			resizeViewport();
			paintCurve(false);
		}

		function reloadPattern() {
			g_Loading = true;
			new Ajax.Request('/api/call', { parameters: { 'getpattern': g_PatternID }, method: 'get', onSuccess: onReloadPattern });
			paintCurve(false);
		}

		function setHelp(txt) {
			var ht = '';
			ht += txt;
			$('pthelp').innerHTML = ht;
		}

		function getPointAt(mx, my) {
			console.log('getPointAt @ ' + mx + ',' + my);

			for (var ai = 0; ai < g_PatternData.automations.length; ai++) {
				var aut = g_PatternData.automations[ai];

				if (aut.channel == g_Channel) {
					for (var pi = 0; pi < aut.points.length; pi++) {
						var pt = aut.points[pi];

						var x = pt.time;
						x = g_Viewport.TtoX(x);
						x = Math.round(x);

						var y = pt.value;
						y = g_Viewport.VtoY(y);
						y = Math.round(y);

						if (mx >= x - 5 && mx <= x + 5 && my >= y - 5 && my <= y + 5) {
							return pi;
						}
					}
				}
			}

			return -1;
		}

		function getCurrentAutomation() {
			for (var ai = 0; ai < g_PatternData.automations.length; ai++) {
				var aut = g_PatternData.automations[ai];
				if (aut.channel == g_Channel) {
					return aut;
				}
			}
			return null;
		}

		function getPointIndex(idx) {
			var aut = getCurrentAutomation();
			if (aut != null)
				return aut.points[idx];
			return null;
		}

		function sendNotification(msg, data) {
			data.channel = g_Channel;
			data.session = g_SessionID;
			data.pattern = g_PatternID;
			console.log('sendNotification', msg, data);
			Seq.Notifications.Send(msg, data);
		}

		function onMouseDblClick(evt) {
			Event.stop(evt);
			var mx = Event.pointerX(evt);
			var my = Event.pointerY(evt) - 40;
			console.log('dblclick @ ' + mx + ',' + my);

			var ptidx = getPointAt(mx, my);
			console.log("dblclicked item " + ptidx);

			if (ptidx == -1) {
				// create new point
				var keyid = Seq.Common.GetRandomID('ak');
				var t = g_Viewport.XtoT(mx);
				var v = g_Viewport.YtoV(my);

				var aut = getCurrentAutomation();

				if (aut == null) {
					// ingen automation existerar, skapa den först
					aut = { channel: g_Channel, points: [] };
					g_PatternData.automations.push(aut);
				}

				aut.points.push({ id: keyid, time: t, value: v });

				sendNotification('createautomationkey', { id: keyid, channel: g_Channel, time: t, value: v });

			}
			else {
				// delete point #ptid

				var pt = getPointIndex(ptidx);

				sendNotification('deleteautomationkey', { id: pt.id });

				var aut = getCurrentAutomation();
				aut.points.removeAt(ptidx);


			}

			g_CurveData = [];
			paintCurve(false);
			reloadCurveData();
		}

		function onMouseDown(evt) {
			Event.stop(evt);
			var mx = Event.pointerX(evt);
			var my = Event.pointerY(evt) - 40;
			console.log('mousedown @ ' + mx + ',' + my);

			g_AnyMovement = false;
			g_Selected = getPointAt(mx, my);

			console.log('selected ' + g_Selected);
		}

		function onMouseMove(evt) {
			Event.stop(evt);
			if (g_Selected != -1) {
				//		paintCurve(true);
				var mx = Event.pointerX(evt);
				var my = Event.pointerY(evt) - 40;
				// console.log('mousemove @ ' + mx + ',' + my);
				var el = $('pt' + g_Selected);
				// console.log(el);
				var pt = getPointIndex(g_Selected);
				if (pt != null) {
					pt.time = g_Viewport.XtoT(mx);
					pt.value = g_Viewport.YtoV(my);

					setHelp('time=' + Math.round( pt.time ) + ', value=' + Math.round( pt.value)) ;
					
					// här kan vi snappa osv!
					el.style.left = (mx - 5) + 'px';
					el.style.top = (my - 5) + 'px';
					g_AnyMovement = true;
				}
			}
		}

		function onMouseUp(evt) {
			Event.stop(evt);
			var mx = Event.pointerX(evt);
			var my = Event.pointerY(evt) - 40;
			console.log('mouseup @ ' + mx + ',' + my);

			if (g_AnyMovement) {
				if (g_Selected != -1) {
					// save position
					var pt = getPointIndex(g_Selected);
					sendNotification('updateautomationkey', { id: pt.id, channel: g_Channel, time: pt.time, value: pt.value });

					// save position!
					g_CurveData = [];
					paintCurve(false);
					reloadCurveData();
				}
			}

			g_Selected = -1;
		}

		function paintCurve(fast) {

			//	console.log('paintCurve();');
			//	console.log('loading=' + g_Loading);
			//	console.log('duration=' + g_PatternData.duration);

			console.log(g_PatternData.automations);

			if (g_Loading)
				setHelp('Loading pattern...');
			else
				setHelp('pat: ' + g_PatternID + ', chan: ' + g_Channel + ', loading: ' + g_Loading);

			var ht = '';
			for (var ai = 0; ai < g_PatternData.automations.length; ai++) {
				var aut = g_PatternData.automations[ai];

				for (var pi = 0; pi < aut.points.length; pi++) {
					var pt = aut.points[pi];

					var x = pt.time;
					x = g_Viewport.TtoX(x);
					x -= 5;
					x = Math.round(x);

					var y = pt.value;
					y = g_Viewport.VtoY(y);
					y -= 5;
					y = Math.round(y);

					console.log(x, y);
					if (aut.channel == g_Channel)
						ht += '<a class=\"pt\" id=\"pt' + pi + '\" style=\"left:' + x + 'px;top:' + y + 'px;\"><span>c' + aut.channel + ' p' + pi + ' v' + Math.round( pt.value ) + ' t' + Math.round( pt.time ) + '</span></a>';
					else
						ht += '<a class=\"ptdim\" style=\"left:' + x + 'px;top:' + y + 'px;\"></a>';
				}
			}



			$('pts').innerHTML = ht;
			/*
			for (var ai = 0; ai < g_PatternData.automations.length; ai++) {
			var aut = g_PatternData.automations[ai];

				for (var pi = 0; pi < aut.points.length; pi++) {

					if (aut.channel == g_Channel) {
			var el = $('pt' + pi);						
			}
			} 
			}
			*/

			//	if (!fast) {
			ht = '';
			for (var t = 0; t <= g_PatternData.duration; t++) {
				var x = Math.round(g_Viewport.TtoX(t * 100));

				ht += '<div class=\"ptguideline\" style=\"left:' + x + 'px;top:' + g_Viewport.basey + 'px;height:' + g_Viewport.height + 'px;\"></div>';

			}

			$('ptguides').innerHTML = ht;


			ht = '';
			if (g_CurveData.length > 0) {
				var w1 = g_Viewport.width / g_CurveData.length;
				var w1i = Math.ceil(w1);
				for (var i = 0; i < g_CurveData.length; i++) {
					var x = Math.floor(w1 * i);
					//	var t = g_Viewport.XtoT(x);
					//	var v = g_ g_Interp.getValue(t);
					var y = Math.round(g_Viewport.VtoY(g_CurveData[i]));

					ht += '<div class=\"ptcurveline\" style=\"left:' + (g_Viewport.basex + x) + 'px;top:' + (y) + 'px;height:' + (g_Viewport.height + g_Viewport.basey  - y) + 'px;width:'+w1i+'px;\"></div>';
				}
			}
			$('ptcurves').innerHTML = ht;

			/*
			ht = '';
			for (var x = 0; x < g_Viewport.width; x += 10) {

					var t = g_Viewport.XtoT(x);
			var v = g_Interp.getValue(t);
			var y = Math.round(g_Viewport.VtoY(v));
					
			ht += '<div class=\"ptcurveline\" style=\"left:' + (g_Viewport.basex + x) + 'px;top:' + (g_Viewport.basey + y) + 'px;height:' + (g_Viewport.height - y) + 'px;width:2px;\"></div>';
			}
			$('ptcurves').innerHTML = ht;
			*/
			//	}
		}

		function onReloadCurveData(r) {
			var d = r.responseText.evalJSON(true);
			g_CurveData = d.channelinterpolation;
			paintCurve(true);
		}

		function reloadCurveData() {
			new Ajax.Request('/api/call', { parameters: { 'getautomationchannelinterpolation': 1, 'pattern': g_PatternID, 'channel': g_Channel }, method: 'get', onSuccess: onReloadCurveData });
		}

		function onChannelChange(e) {
			g_Channel = Seq.UI.GetDropdownValue($('channels'), g_Channel);
			g_CurveData = [];
			paintCurve(false);
			reloadCurveData();
		}

		function onPatternChange(e) {
			g_PatternID = Seq.UI.GetDropdownValue($('patterns'), g_PatternID);
			g_PatternData = { duration: 16, automations: [] };
			g_CurveData = [];
			reloadPattern();  
			reloadCurveData();
		}

		function onWindowResize(e) {
			resizeViewport();
			paintCurve(false);
		}

		function resizeViewport() {
			var ww = 400;
			var wh = 600;

			var dd = $('main').getDimensions();
			console.log(dd);

			ww = dd.width - 40;
			wh = dd.height - 40;

			g_Viewport.reset();
			g_Viewport.width = ww;
			g_Viewport.beatwidth = ww / g_PatternData.duration;
			g_Viewport.duration = g_PatternData.duration * 100;
			g_Viewport.headersize = 0;
			g_Viewport.basex = 20;
			g_Viewport.basey = 20;
			g_Viewport.height = wh;

			$('ptbg').style.left = '20px';
			$('ptbg').style.top = '20px';
			$('ptbg').style.width = ww + 'px';
			$('ptbg').style.height = wh + 'px';
		}

		function initCurveEditor() {

			g_Viewport = new Seq.Viewport();

			g_Channel = 0;
			g_PatternData = { duration: 16, automations: [] };
			g_PatternID = Seq.State.GetCurrentPatternID();
			g_SessionID = Seq.State.GetCurrentSessionID();
			g_InstrumentID = Seq.State.GetCurrentInstrumentID();
			g_Interp = new Seq.CurveInterpolator();

			Event.observe($('patterns'), 'change', onPatternChange.bindAsEventListener(this));
			Event.observe($('channels'), 'change', onChannelChange.bindAsEventListener(this));

			Event.observe($('main'), 'mousedown', onMouseDown.bindAsEventListener(this));
			Event.observe($('main'), 'mousemove', onMouseMove.bindAsEventListener(this));
			Event.observe($('main'), 'mouseup', onMouseUp.bindAsEventListener(this));
			Event.observe($('main'), 'dblclick', onMouseDblClick.bindAsEventListener(this));

			Event.observe(window, 'resize', onWindowResize.bindAsEventListener(this));

			resizeViewport();
			reloadPatternList();
			reloadAutomationChannelList();
			reloadPattern();
			reloadCurveData();
			// paintCurve(false);

			Seq.Notifications.StartListening();
		}
	
	</script>

</head>
<body onload="initCurveEditor()">
	<div id="header">
		<p>
			<b>Automation editor</b> | Channel:
			<select id="channels" style="width: 100px;">
			</select>
			| Pattern:
			<select id="patterns" style="width: 200px;">
			</select>
		</p>
	</div>
	<div id="main">
		<div class="inner">
			<div id="ptbg">
			</div>
			<div id="ptcurves">
			</div>
			<div id="ptguides">
			</div>
			<div id="pts">
			</div>
			<div id="pthelp">
			</div>
		</div>
	</div>
</body>
</html>
